// @author U099151W Sun Chenchen
// all functions in this file  are implemented by the author unless specifically indicated
var Map = function() {
    var _row;
    var _col;

    var _currentGridList = [];
    var _adjcentGridList = [];

    this.getMapSize = function() {
        return {
            row : _row,
            col : _col
        };
    };

    // join two matrix horizontally
    function matrixHorizontalConcat(a, b) {
        var c = [];
        for (var i=0; i<a.length; i++) {
            c[i] = a[i].concat(b[i]);
        }
        return c;
    }
    function matrixVerticalConcat(a, b) {
        var c = [];
        for (var i=0; i<a.length; i++) {
            c.push(a[i]);
        }
        for (var i=0; i<b.length; i++) {
            c.push(b[i]);
        }
        return c;
    }

    this.getObstacleMap = function(gridStartX, gridStartY, gridEndX, gridEndY) {
        var obstacleMap = [];
        for (var i= gridStartY; i <= gridEndY; i++) {
            var newRow = [];
            for (var j=gridStartX; j <= gridEndX; j++) {
                if (newRow.length === 0) {
                    newRow = map.getGridById(i, j).getObstacleMap();
                } else {
                    newRow = matrixHorizontalConcat(newRow,  map.getGridById(i, j).getObstacleMap());
                }
            }
            if (obstacleMap.length === 0) {
                obstacleMap = newRow;
            } else {
                obstacleMap =   matrixVerticalConcat(obstacleMap,newRow);
            }
        }
        return obstacleMap;
    };

    function toWorldCoord(gridX, gridY, x, y) {
        var gridSize = _grids[0][0].getGridSize();

        var wx = gridX * gridSize.col + x;
        var wy = gridY * gridSize.row + y;
        return {
            x : wx,
            y : wy
        };
    }

    function toLocalCoord(x, y) {
        var gridSize = _grids[0][0].getGridSize();
        var gridX = Math.floor(x / gridSize.col);
        var gridY = Math.floor(y / gridSize.row);
        var lx = x % gridSize.col;
        var ly = y % gridSize.row;
        return {
            gridX : gridX,
            gridY : gridY,
            x : lx,
            y : ly
        };
    }

    this.updateGridsLevel = function(args) {
        var sx = args.sx;
        var sy = args.sy;
        var w = args.w-1;
        var h = args.h-1;
        var startPoint = toLocalCoord(sx, sy);
        var endPoint = toLocalCoord(sx+w, sy+h);

        var extendStartPointX = startPoint.gridX - 1 >= 0 ? startPoint.gridX -1 : 0;
        var extendStartPointY = startPoint.gridY - 1 >= 0 ? startPoint.gridY -1 : 0;
        var extendEndPointX = endPoint.gridX + 1;
        var extendEndPointY = endPoint.gridY + 1;

        _currentGridList = [];
        _adjcentGridList = [];
        for (var i=extendStartPointX; i<=extendEndPointX; i++) {
            for (var j=extendStartPointY; j<=extendEndPointY; j++) {
                if (i >= startPoint.gridX && i <= endPoint.gridX &&
                    j >= startPoint.gridY && j <= endPoint.gridY) {
                    _currentGridList.push(this.getGridById(j, i));
                } else {
                    _adjcentGridList.push(this.getGridById(j, i));
                }
            }
        }
    };

    var _grids;
    this.getAllGrids = function() {
        return _grids;
    };
    this.getGridById = function(row, col) {
        return _grids[row][col];
    };
    this.getCurrentGrids = function() {
        return _currentGridList;
    };
    this.getAdjcentGrids = function() {
        return _adjcentGridList;
    };
    this.setCurrentGrids = function() {

    };

    this.drawDebug = function(canvas, sx, sy, w, h) {
        var gridWidth = w / _row;
        var gridHeight = h / _col;

        var context = canvas.getContext("2d");
        context.save();
        context.translate(sx, sy);
        var x, y;
        for (var i=0; i<_row; i++) {
            for (var j=0; j<_col; j++) {
                x = gridWidth * j;
                y = gridHeight * i;

                context.save();
                context.translate(x, y);
                context.fillStyle = "blue";
                context.fillRect(0, 0, gridWidth, gridHeight);

                //context.fillText((i*mapSize.col+j).toString(), 0, DEBUG_GRID_SIZE);
                context.restore();
            }
        }

        for (var i=0; i<_currentGridList.length; i++) {

            x = gridWidth * _currentGridList[i].getIndexOfGrid().colIndex;
            y = gridHeight * _currentGridList[i].getIndexOfGrid().rowIndex;

            context.save();
            context.translate(x, y);
            context.fillStyle = "red";
            context.fillRect(0, 0, gridWidth, gridHeight);
            //context.fillText((i*mapSize.col+j).toString(), 0, DEBUG_GRID_SIZE);
            context.restore();
        }
        for (var i=0; i<_adjcentGridList.length; i++) {

            x = gridWidth * _adjcentGridList[i].getIndexOfGrid().colIndex;
            y = gridHeight * _adjcentGridList[i].getIndexOfGrid().rowIndex;

            context.save();
            context.translate(x, y);
            context.fillStyle = "orange";
            context.fillRect(0, 0, gridWidth, gridHeight);
            //context.fillText((i*mapSize.col+j).toString(), 0, DEBUG_GRID_SIZE);
            context.restore();
        }
        for (var i=0; i<_row; i++) {
            for (var j=0; j<_col; j++) {
                x = gridWidth * j;
                y = gridHeight * i;

                context.save();
                context.translate(x, y);
                context.strokeStyle = "black";
                context.lineWidth = 1;
                context.strokeRect(0, 0, gridWidth, gridHeight);
                var grid = this.getGridById(i, j);
                context.fillStyle = "white";
                context.fillText(grid.getType().substr(0, 2).toUpperCase(), 2, 10);
                //context.fillText((i*mapSize.col+j).toString(), 0, DEBUG_GRID_SIZE);
                context.restore();
            }
        }


        context.restore();
    };

    this.init = function(args) {
        _row = args.row;
        _col = args.col;
        _grids = [];
        for (var i=0; i<_row; i++) {
            var newRow = [];
            for (var j=0; j<_col; j++) {
                var newGrid = new Grid();
                newGrid.init({
                    col : 10,
                    row : 10,
                    map : this,
                    indexOfRow : i,
                    indexOfCol : j,
                    type : mapSymbol[cityMap[i][j]],
                    NPCs : args.grids[i][j].NPCs
                });
                // set buildings to grid
                var buildings = BuildingList[mapSymbol[cityMap[i][j]]];
                for (var k=0; k < buildings.length; k++) {
                    var newBuilding = new Building();
                    newBuilding.init({
                        col : 10,
                        row : 10,
                        map : newGrid,
                        indexOfRow : 0,
                        indexOfCol : 0,
                        type : buildings[k].type,
                        NPCs : {}
                    });
                    newBuilding.setPortals(buildings[k].insidePortals);
                    newBuilding.setGrid(newGrid);
                    newBuilding.setOutsidePortals(buildings[k].portals);
                    newGrid.setNewBuilding({
                        portals : buildings[k].portals,
                        obj : newBuilding
                    })
                }
                newRow.push(newGrid);
            }
            _grids.push(newRow);
        }
        this.updateGridsLevel(camera.getCameraParameter());
    };
};
var map = new Map();